/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.acworks.acroute.impl;

import com.acworks.acroute.Datacenter;
import com.acworks.acroute.InvalidDatacenterDefinitionException;
import com.acworks.acroute.Space;
import com.acworks.acroute.SpaceType;
import com.acworks.acroute.Util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author nikita
 */
public class DatacenterImpl implements Datacenter {

    private final Set<Space> ownedRooms;
    private final Space startingSpace;
    final private int width;
    final private int height;
    final private Space[][] spaceGrid;
    final private Map<Space, List<Space>> adjacentSpacesCache;

    /**
     *
     * @param width width of datacenter grid
     * @param height height of datacenter grid
     * @param gridIntegers input integers denoting ds rooms (size == width * height)
     */
    public DatacenterImpl(Space[][] spaceGrid) {
        assert spaceGrid != null;

        this.width = spaceGrid.length;
        this.height = spaceGrid[0].length;
        if (width <= 0 || height <= 0) {
            throw new InvalidDatacenterDefinitionException("invalid DS width or height");
        }
        this.spaceGrid = spaceGrid;

        this.adjacentSpacesCache = new HashMap<Space, List<Space>>();

        Set<Space> allSpaces = new HashSet<Space>(width * height);
        for (Space[] column : spaceGrid) {
            for (Space space : column) {
                allSpaces.add(space);
            }
        }

        //cache ownedRooms, startingSpace to avoid repeat traversals of the grid
        ownedRooms = Util.findSpacesByType(allSpaces, SpaceType.OWNED_ROOM);
        startingSpace = Util.findSpacesByType(allSpaces, SpaceType.START_SPACE).iterator().next();
    }

    @Override
    public Set<Space> getOwnedRooms() {
        return ownedRooms;
    }

    @Override
    public Space getStartingSpace() {
        return startingSpace;
    }

    @Override
    public Space getSpaceAt(int x, int y) {
        Space s = getSpaceIfExistsAt(x, y);
        if (s == null) {
            throw new IllegalArgumentException("no space at x=" + x + ", y=" + y);
        } else {
            return s;
        }
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return height;
    }

    @Override
    public List<Space> getSpacesAdjacentTo(Space space) {
        assert space != null;

        //search results can be cached as Map<Space,List<Space>>

        List<Space> adjacentSpaces = adjacentSpacesCache.get(space);
        if (adjacentSpaces == null) {
            synchronized (adjacentSpacesCache) {
                adjacentSpaces = doGetSpacesAdjacentTo(space);
                adjacentSpacesCache.put(space, adjacentSpaces);
            }
        }

        return adjacentSpaces;
    }

    private List<Space> doGetSpacesAdjacentTo(Space space) {
        assert space != null;
        final List<Space> adjacentSpaces = new ArrayList<Space>(4);
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (space.equals(spaceGrid[i][j])) {
                    //space at i,j --> compute coordinates of adjacent spaces

                    //above
                    Space above = getSpaceIfExistsAt(i, j - 1);
                    if (above != null) {
                        adjacentSpaces.add(above);
                    }

                    //to the right
                    Space rightOf = getSpaceIfExistsAt(i + 1, j);
                    if (rightOf != null) {
                        adjacentSpaces.add(rightOf);
                    }

                    //below
                    Space below = getSpaceIfExistsAt(i, j + 1);
                    if (below != null) {
                        adjacentSpaces.add(below);
                    }

                    //to the left
                    Space leftOf = getSpaceIfExistsAt(i - 1, j);
                    if (leftOf != null) {
                        adjacentSpaces.add(leftOf);
                    }
                }
            }
        }
        return adjacentSpaces;
    }

    /**
     *
     * @param i
     * @param j
     * @return Space only if one exists at i,j coordinate of the spaceGrid
     */
    private Space getSpaceIfExistsAt(int x, int y) {
        if ((x < 0) || (x > (width - 1)) || (y < 0) || (y > (height - 1))) {
            return null;
        } else {
            return spaceGrid[x][y];
        }
    }

    @Override
    public int getOwnedRoomsCount() {
        return ownedRooms.size();
    }
}
